<template>
    <div class="page-form-generator">
        <div class="form-drag" onselectstart="return false">
            <a-card size="small" :bordered="false">
                <template #title>
                    <span style="font-size: 14px;"><control-outlined />&nbsp;&nbsp;输入型控件</span>
                </template>
                <draggable
                    class="components-draggable"
                    :list="inputDragMyList"
                    :group="{ name: 'componentsGroup', pull: 'clone', put: false }"
                    item-key="formItemId"
                    :clone="cloneComponent"
                    handle=".tagComp"
                    :sort="false"
                    @end="onEnd"
                    chosen-class="fallboard"
                    :force-fallback="true"
                >
                    <template #item="{ element }">
                        <div class="tagComp" @click="addComponent(element)">
                            <span class="icon">
                                <template v-if="element?.isSvg">
                                    <img :src="getSvgURL(element?.tagIcon)" />
                                </template>
                                <template v-else>
                                    <component :is="element?.tagIcon"></component>
                                </template>
                            </span>
                            <span>{{ element?.name }}</span>
                        </div>
                    </template>
                </draggable>
            </a-card>
            <a-card size="small" :bordered="false">
                <template #title>
                    <span style="font-size: 14px;"><control-outlined />&nbsp;&nbsp;选择型控件</span>
                </template>
                <draggable
                    class="components-draggable"
                    :list="chooseDragMyList"
                    :group="{ name: 'componentsGroup', pull: 'clone', put: false }"
                    item-key="formItemId"
                    :clone="cloneComponent"
                    handle=".tagComp"
                    :sort="false"
                    @end="onEnd"
                    chosen-class="fallboard"
                    :force-fallback="true"
                >
                    <template #item="{ element }">
                        <div class="tagComp" @click="addComponent(element)">
                            <span class="icon">
                                <template v-if="element?.isSvg">
                                    <img :src="getSvgURL(element?.tagIcon)" />
                                </template>
                                <template v-else>
                                    <component :is="element?.tagIcon"></component>
                                </template>
                            </span>
                            <span>{{ element?.name }}</span>
                        </div>
                    </template>
                </draggable>
            </a-card>
        </div>
        <div class="form-draw-wrapper" onselectstart="return false">
            <div class="drawhead" v-if="drawingList.length">
                <a-space>
                    <a-button shape="round" @click="viewNewForm">
                        <template #icon><eye-outlined /></template>
                        预览
                    </a-button>
                    <a-button type="primary" shape="round" danger @click="ClearComponent">
                        <template #icon><clear-outlined /></template>
                        清空
                    </a-button>
                    <a-button type="primary" shape="round" @click="onSaveBtn">
                        <template #icon><save-outlined /></template>
                        保存
                    </a-button>
                </a-space>
            </div>
            <div class="form-draw">
                <a-form
                    v-bind="getProps"
                    :model="formModel"
                >
                    <draggable
                        :class="drawingList.length ? 'drawing-board' : 'drawing-occupy'"
                        tag="a-row"
                        :component-data="getComponentData()"
                        :list="drawingList" 
                        item-key="formId"
                        :animation="340"
                        handle=".item"
                        group="componentsGroup"
                    >
                        <template #item="{ element }">
                            <a-col
                                v-bind="getColProps(element)"
                                :class="activeData?.formId == element?.formId || activeId == element?.formId ? 'item item_active' : 'item'"
                                @mouseover="onMouseOver(element)"
                                @mouseleave="onMouseLeave"
                                @click="selectComponent(element)"
                            >
                                <form-item
                                    :formProps="getProps"
                                    :schema="getFormSchema(element)"
                                    :allDefaultValues="defaultValueRef"
                                    :formModel="formModel"
                                    :setFormModel="setFormModel"
                                />
                                <div class="operator" v-if="activeData?.formId == element?.formId || activeId == element?.formId">
                                    <a-space>
                                        <a-button type="primary" shape="circle" size="small" @click.stop="onCopyComponent(element)">
                                            <template #icon><copy-outlined /></template>
                                        </a-button>
                                        <a-button type="primary" shape="circle" danger size="small" @click.stop="onDelComponent(element)">
                                            <template #icon><delete-outlined /></template>
                                        </a-button>
                                    </a-space>
                                </div>
                            </a-col>
                        </template>
                    </draggable>
                    <div class="drawresult" v-if="!drawingList.length">
                        <appstore-two-tone style="font-size: 80px;" />
                        <div class="result-info">从左侧拖入或点选组件进行表单设计</div>
                    </div>
                </a-form>
            </div>
        </div>
        <div class="form-attr">
            <a-tabs v-model:activeKey="tabActKey">
                <a-tab-pane :key="1" tab="控件属性">
                    <CompPanel :config="activeData" @change="setupCompChange" />
                </a-tab-pane>
                <a-tab-pane :key="2" tab="表单属性">
                    <FormPanel :config="getProps" @change="setupFormChange" />
                </a-tab-pane>
            </a-tabs>
        </div>

        <PreviewForm ref="viewRef" />
    </div>
</template>

<script lang="ts">
import { ControlOutlined, AppstoreTwoTone, CopyOutlined, DeleteOutlined, ClearOutlined, EyeOutlined, SaveOutlined } from '@ant-design/icons-vue';
import { Modal } from 'ant-design-vue';
import draggable from 'vuedraggable';
import { formConfig, baseComponents, chooseComponents, defaultRanges } from './componentMap';
import { useSchemas } from './hooks/useSchemas';
import { deepClone } from '@/utils';
import type { PropType } from 'vue';

import FormItem from './components/FormItem.vue';

const FormPanel = defineAsyncComponent(() => import('./components/FormPanel.vue'));
const CompPanel = defineAsyncComponent(() => import('./components/CompPanel/index.vue'));
const PreviewForm = defineAsyncComponent(() => import('./components/PreviewForm/index.vue'));

// eslint-disable-next-line @typescript-eslint/no-unused-vars
let tempActiveData:Recordable = {}; // 暂存数据

export default defineComponent({
    name: 'FormGenerator',
    components: {
        ControlOutlined,
        AppstoreTwoTone,
        CopyOutlined,
        DeleteOutlined,
        ClearOutlined,
        EyeOutlined,
        SaveOutlined,
        draggable,
        FormItem,
        FormPanel,
        CompPanel,
        PreviewForm,
    },
    props: {
        data: {
            type: Object as PropType<Recordable>,
            default: () => ({}),
        },
    },
    emits: ['save'],
    setup(props, { emit }){
        // 输入组件拖拽列表
        const inputDragMyList = computed(() => {
            const list = baseComponents.map((item: any) => {
                return {
                    name: item?._config_?.label,
                    ...item?._other_,
                    ...item,
                };
            });
            return list;
        });
        // 选择组件拖拽列表
        const chooseDragMyList = computed(() => {
            const list = chooseComponents.map((item: any) => {
                return {
                    name: item?._config_?.label,
                    ...item?._other_,
                    ...item,
                };
            });
            return list;
        });

        const drawingList = ref<Recordable[]>([]); // 目标画布表单列表

        const activeData = reactive<Recordable>({});
        const activeId = ref<string>('');

        const formModel = reactive<Recordable>({}); // 表单绑定值
        const defaultValueRef = ref<Recordable>({}); // 表单默认值

        const tabActKey = ref<number>(1); // 标签页切换绑定值

        const viewRef = ref<Nullable<any>>(); // 预览

        const getSvgURL = (svgName: string) => {
            return new URL(`../../../assets/svg/${svgName}.svg`, import.meta.url).href;
        };
        
        const activeFormItem = (currentItem: Recordable) => {
            if(Object.keys(activeData).length > 0){
                Object.keys(activeData).forEach((key:string) => delete activeData[key]);
            }
            Object.assign(activeData, currentItem);
        };

        // 拖拽结束事件
        const onEnd = (next: Recordable) => {
            if (next?.from !== next?.to) {
                addComponent(tempActiveData, next?.newIndex);
            }
        };

        // clone时拖拽的回调函数
        const cloneComponent = (origin: Recordable) => {
            const clone = deepClone(origin);
            tempActiveData = clone;
        };

        // 添加组件事件
        const addComponent = (record: Recordable, newIdx=-1) => {
            const clone = deepClone(record) as Recordable;
            if(!clone?._config_?.field){
                createIdAndKey(clone);
            }
            if(newIdx >= 0 && unref(drawingList).length){
                drawingList.value.splice(newIdx, 0, clone);
            }else{
                drawingList.value.push(clone);
            }
            console.log('触发', unref(drawingList));
            activeFormItem(clone);
        };

        // 复制组件事件
        const onCopyComponent = (record: Recordable) => {
            const clone = deepClone(record) as Recordable;
            createIdAndKey(clone);
            addComponent(clone);
            tempActiveData = clone;
            activeFormItem(clone);
        };

        const createIdAndKey = (record: Recordable) => {
            record._config_.field = 'field_'+Math.floor(Math.random() * 1000);
            record.formId = 'field_'+Math.floor(Math.random() * 1000)+'_'+record?.formItemId;
        };

        // 删除组件事件
        const onDelComponent = (record: Recordable) => {
            unref(drawingList).forEach((item:any, idx: number) => {
                if(record?.formId == item?.formId){
                    drawingList.value.splice(idx, 1);
                }
            });
            if(!unref(drawingList).length){
                activeFormItem({});
                activeId.value = '';
            }else{
                if(record?.formId == unref(activeId)){
                    const idx = unref(drawingList).length - 1;
                    const newAct = deepClone(unref(drawingList)[idx]) as Recordable;
                    activeFormItem(newAct);
                    activeId.value = newAct?.formId;
                }
            }
        };

        // 鼠标移入移出事件
        const onMouseOver = (record: Recordable) => {
            activeId.value = record?.formId;
        };
        const onMouseLeave = () => {
            activeId.value = '';
        };

        // 选中组件事件
        const selectComponent = (record: Recordable) => {
            const clone = deepClone(record) as Recordable;
            tempActiveData = clone;
            activeFormItem(clone);
        };

        // 清空组件事件
        const ClearComponent = () => {
            Modal.confirm({
                title: '确定要清空所有组件吗？',
                cancelText: '取消',
                onOk: () => {
                    activeFormItem({});
                    drawingList.value = [];
                    activeId.value = '';
                },
            });
        };

        // 行配置项
        const getComponentData = () => {
            const { gutter } = formConfig;
            return { gutter };
        };

        const getColProps = (record:Recordable) => {
            const { baseColProps } = formConfig;
            const { _config_ } = record;
            if(_config_?.colProps?.span){
                return { xs: 24, sm: 24, md: 24, lg: _config_?.colProps?.span, xl: _config_?.colProps?.span, xxl: _config_?.colProps?.span };
            }
            return baseColProps;
        };

        const getProps = computed(() => formConfig as any);

        // 获取表单组件数据
        const getFormSchema = (comp: Recordable) => {
            const { _config_, _props_ }: Recordable = toRaw(comp);
            if(_config_?.component == 'RangePicker'){
                _props_.ranges = defaultRanges();
            }
            return toRaw({ ..._config_, componentProps: _props_ });
        };

        const setFormModel = (key: string, value: any) => {
            formModel[key] = value;
        };

        // 设置表单属性
        const setupFormChange = (form: Recordable) => {
            Object.assign(formConfig, form);
        };

        // 设置组件属性
        const setupCompChange = (record: Recordable) => {
            const clone = deepClone(record) as Recordable;
            const idx = unref(drawingList).findIndex((item:any) => item?.formId == activeData?.formId);
            if(idx != -1){
                drawingList.value.splice(idx, 1, clone); // 替换内容
            }
            // 设置默认值
            defaultValueRef[record?._config_?.field] = record?._config_?.defaultValue;
            setFormModel(record?._config_?.field, record?._config_?.defaultValue);

            console.log('设置：',unref(activeId), clone);
            activeFormItem(clone);
            
            activeId.value = clone?.formId;
        };

        // 预览设计的表单事件
        const viewNewForm = () => {
            const schemas = deepClone(useSchemas((unref(drawingList) as any[]))) as any[];
            schemas.forEach((item:any) => {
                if(item?.component == 'RangePicker'){
                    item.componentProps.ranges = defaultRanges();
                }
            });
            unref(viewRef)?.open(schemas, formConfig);
        };

        // 保存按钮事件
        const onSaveBtn = () => {
            const formState:Recordable = {};
            unref(drawingList).forEach((item: any) => {
                const key = item?._config_?.field;
                formState[key] = null;
            });
            emit('save', unref(drawingList), formState, unref(getProps));
        };

        watch(() => props.data, (val: Recordable) => {
            const configList = deepClone(unref(val[formConfig?.name])) as Recordable || []; 
            const formState = unref(val[formConfig?.formModel]) || {};
            if(configList.length > 0){
                drawingList.value = configList as any[];
                tempActiveData = drawingList.value[unref(drawingList).length -1];
                activeId.value = tempActiveData?.formId;
                activeFormItem(tempActiveData);
            }
            if(Object.keys(formState).length > 0){
                Object.keys(formState).forEach((key: string) => {
                    formModel[key] = formState[key] ? formState[key] : undefined;
                });
            }
            if(val?.config && Object.keys(val?.config).length > 0){
                Object.assign(formConfig, val?.config);
            }
        }, { immediate: true, deep: true });

        return {
            inputDragMyList,
            chooseDragMyList,
            drawingList,
            activeData,
            activeId,
            formModel,
            defaultValueRef,

            getSvgURL,
            cloneComponent,
            getComponentData,
            onCopyComponent,
            addComponent,
            onDelComponent,
            selectComponent,
            onEnd,
            onMouseOver,
            onMouseLeave,
            ClearComponent,

            getProps,
            getColProps,
            getFormSchema,
            setFormModel,

            tabActKey,
            setupFormChange,
            setupCompChange,
            
            viewRef,
            viewNewForm,
            onSaveBtn,
        };
    },
});
</script>

<style lang="less" scoped>
.page-form-generator {
    height: 100%;
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    overflow: hidden;
    overflow-x: auto;
    .form-drag {
        background-color: #fff;
        min-width: 250px;
        width: 250px;
        height: 100%;
        overflow-y: auto;

        .components-draggable {
            width: 100%;
            display: flex;
            flex-direction: row;
            align-items: center;
            flex-wrap: wrap;
            .tagComp {
                width: 47%;
                margin: 3px;
                padding: 8px 10px;
                background: #f6f7ff;
                font-size: 12px;
                cursor: move;
                border: 1px dashed #f6f7ff;
                border-radius: 3px;
                box-sizing: border-box;
                display: flex;
                align-items: center;
                &:hover{
                    border: 1px dashed #787be8;
                    color: #787be8;
                }
                .icon {
                    padding-right: 4px;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                }
            }
        }
    }

    .form-draw-wrapper {
        flex: 1;
        display: flex;
        flex-direction: column;
        height: 100%;
        overflow: hidden;
        .drawhead {
            background-color: #fff;
            margin: 0 8px 8px 8px;
            padding: 6px;
            display: flex;
            justify-content: flex-end;
            align-items: center;
        }
        .form-draw {
            margin: 0 8px;
            background-color: #fff;
            flex: 1;
            min-width: 350px;
            display: flex;
            flex-direction: column;
            height: 100%;
            overflow-y: auto;
            padding: 20px;
            position: relative;
            .drawing-board {
                width: 100%;
                .item {
                    padding: 14px 0;
                    margin-bottom: 10px;
                    cursor: move;
                    position: relative;
                    &:hover {
                        background-color: #f6f7ff;
                        border-radius: 4px;
                    }
                    .operator {
                        position: absolute;
                        top: -12px;
                        right: 8px;
                        z-index: 99;
                    }
                }
                .item_active {
                    background-color: #f6f7ff;
                    border-radius: 4px;
                }
            }
            .ant-form {
                :deep(.ant-form-item) { margin-bottom: 10px; }
            }

            .drawing-occupy {
                width: 100%;
                height: 450px;
                border: 1px dashed #d9d9d9;
            }
            .drawresult{
                position: absolute;
                top: 150px;
                left: 0;
                z-index: 9;
                width: 100%;
                display: flex;
                flex-direction: column;
                justify-content: center;
                align-items: center;
                .result-info { font-size: 18px; margin-top: 12px; }
            }
        }
    }
    .form-attr {
        background-color: #fff;
        min-width: 350px;
        width: 350px;
        height: 100%;
        overflow: hidden;
        display: flex;
        flex-direction: column;
        .ant-tabs{
            flex: 1;
            height: 100%;
            :deep(.ant-tabs-nav){
                padding: 0 12px;
            }
            :deep(.ant-tabs-content-holder) {
                flex: 1;
                height: 100%;
                overflow-y: auto;
                padding: 0 12px 12px;
            }
        }
    }

    .fallboard {
        background-color: #f6f7ff;
        border-top: 5px solid #1890ff;
        height: 50px;
        width: 100%;
        padding-left: 12px;
        margin-bottom: 10px;
    }
}

// 解决拖拽时会复制文字
*{ 
    -webkit-touch-callout:none; /*系统默认菜单被禁用*/ 
    -webkit-user-select:none; /*webkit浏览器*/ 
    -khtml-user-select:none; /*早期浏览器*/ 
    -moz-user-select:none;/*火狐*/ 
    -ms-user-select:none; /*IE10*/ 
    user-select:none; 
} 
input{ 
    -webkit-user-select:auto; /*webkit浏览器*/  
}
textarea{
    -webkit-user-select:auto; /*webkit浏览器*/
}
</style>